feat: provision the guest-browser MCP server for all agents - #223
Conversation
The runtime installer now seeds /usr/local/bin/andcode-browser-mcp.py (stdlib-only MCP server, no third-party deps) into both the Alpine and Antigravity rootfses and registers it in each agent's config: - OpenCode: ~/.config/opencode/opencode.json (mcp.and-code-browser) - Claude: ~/.claude.json (mcpServers.and-code-browser) - Antigravity: ~/.gemini/config/mcp_config.json Provisioning is idempotent, preserves user-added servers, and also runs on startup for runtimes installed before this change, so OpenCode, Claude Code and Antigravity all expose the same browser_* tools. Supersedes the guest-tools script from #222.
|
🔍 OpenCodeReview found 6 issue(s) in this PR.
|
| sock.sendall(b"GET /json HTTP/1.1\r\nHost: localhost\r\n\r\n") | ||
| data = b"" | ||
| while b"\r\n0\r\n\r\n" not in data and not data.endswith(b"]"): | ||
| while not data.endswith(b"]"): |
There was a problem hiding this comment.
[bug · high]
読み取りループの終了条件変更は回帰リスクがあります。既存の b"\r\n0\r\n\r\n" によるchunked終端の解析コード(下の if b"\r\n0\r\n\r\n" in body:)が残っていることから、WebView devtools の /json レスポンスはchunked転送エンコーディングで返される想定です。その場合レスポンスは ] の後に 0\r\n\r\n が続くため data.endswith(b"]") が成立せず、再び sock.recv() に入ります。HTTP keep-aliveで接続が維持されると _connect() で設定された10秒のタイムアウトまでブロックし、socket.timeout が発生して browser_navigate/browser_info など全CDPツール呼び出しが遅延または失敗します。旧条件 b"\r\n0\r\n\r\n" not in data を復元することを推奨します。
Suggestion:
| while not data.endswith(b"]"): | |
| while b"\r\n0\r\n\r\n" not in data and not data.endswith(b"]"): |
| continue | ||
| try: | ||
| msg = json.loads(line) | ||
| except json.JSONDecodeError: |
There was a problem hiding this comment.
[other · low]
JSONとしてパースできない入力行を無言で捨てています。JSON-RPC仕様ではparse errorは id: null で -32700 Parse error を応答すべきで、現状はクライアントが応答待ちのままタイムアウトする可能性があります。応答を返してから continue するのが望ましいです(少なくともエラーを無視した事実が検知できるようになります)。
Suggestion:
| except json.JSONDecodeError: | |
| except json.JSONDecodeError: | |
| respond({"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}}) | |
| continue |
| }, | ||
| } | ||
| ) | ||
| elif mid is not None: |
There was a problem hiding this comment.
[bug · medium]
MCPプロトコル(2024-11-05)ではサーバーは ping リクエストに応答することが必須とされています。標準的なMCP SDKクライアントは接続維持・疎通確認のために定期的に ping を送信しますが、現状はこの分岐に該当せず -32601 method not found を返します。クライアントによってはping失敗をプロトコル異常とみなしてセッションを切断するため、ping に対して {"jsonrpc":"2.0","id":mid,"result":{}} を返すハンドラを追加してください。
Suggestion:
| elif mid is not None: | |
| elif method == "ping": | |
| respond({"jsonrpc": "2.0", "id": mid, "result": {}}) | |
| elif mid is not None: |
| mergeJsonConfig(File(rootfs, "root/.gemini/config/mcp_config.json")) { root -> | ||
| val servers = root.optJSONObject("mcpServers") ?: JSONObject() | ||
| servers.put(BROWSER_MCP_NAME, browserMcpEntry("antigravity")) | ||
| root.put("mcpServers", servers) | ||
| } |
There was a problem hiding this comment.
[maintainability · medium]
プロジェクト全体は kotlinx.serialization を標準で使用していますが、新規コードのみ org.json (JSONObject/JSONArray) を導入しています。さらに Antigravity の mcp_config.json は既に AntigravityMcp オブジェクトが同じパスを管理しており(command/serverUrl 形式・一時ファイル→rename の原子的書き込み)、このコードは同じ設定を別ライブラリで重複更新しています。書き込み方式や形式の差異により将来ハンドリングが分離し、保守コストが増えます。既存の kotlinx.serialization を利用し、antigravity 向けは AntigravityMcp の既存ヘルパーを再利用することを推奨します。
| file.parentFile?.mkdirs() | ||
| val root = | ||
| if (file.isFile) { | ||
| runCatching { JSONObject(file.readText()) }.getOrNull() ?: JSONObject() |
There was a problem hiding this comment.
[bug · medium]
mergeJsonConfig は JSON パース失敗を runCatching で捕捉して空の JSONObject に置き換え、その後 mutate で内容が変われば既存ファイルを直接上書き writeText します。このため、ユーザーが手動編集した不正 JSON の設定が失われる可能性があります。また writeText は原子的でないため、書き込み途中でプロセスが中断するとルートfs内の設定ファイルが壊れます。既存 AntigravityMcp.writeServers のように「テンポラリへ書き出して rename」する原子的書き込みを行い、パース失敗時はログを残すか、失敗時に書き込みをスキップすることを推奨します。
| val before = root.toString() | ||
| mutate(root) | ||
| if (root.toString() != before) { |
There was a problem hiding this comment.
[maintainability · low]
変更検知を JSONObject.toString() の文字列比較で行っています。Android の org.json は挿入順を保持するため通常は機能しますが、将来の実装やライブラリ差し替えでキー順序が変わる可能性があり、同じ内容でも毎回書き込みが発生する壊れやすい作りです。root.has(BROWSER_MCP_NAME) でキー存在を確認してから書き込むか、similar() 等の意味比較を用いる方が堅牢です。
Seeds
/usr/local/bin/andcode-browser-mcp.py(stdlib-only MCP server) into the Alpine and Antigravity rootfses at install time and registers it with every agent so OpenCode, Claude Code and Antigravity all expose the samebrowser_*tools:~/.config/opencode/opencode.json~/.claude.json~/.gemini/config/mcp_config.jsonProvisioning is idempotent, preserves user-added MCP servers, and re-runs on runtime start so existing installs pick it up without a reinstall. Supersedes the guest-tools script from #222.